/**
 * 
 */
package br.ufpr.inf.jobtuner.pso;

import java.util.ArrayList;
import java.util.Random;

/**
 * @author Edson
 * 
 */
public class PSOptimizer {
	ArrayList<PSOParticle> particles = new ArrayList<PSOParticle>();
	static Integer globalBestStabilizedCounter = 0; 
	static Integer globalBest = null;
	Random random = new Random();
	static double min = -2; 
	static double max = 2;

	public void execute(Integer maxIterations, Integer numberOfParticles) {
		boolean stop = false; 
		initializeParticles(numberOfParticles);
		Integer iteration = 0;
		do {
			calculateAllFitness();
			updateParticlesBest();
			stop = updateGlobalBest();
			moveParticles();
			showProgress(iteration, numberOfParticles);
			
			/* updateGlobalBest return true 
			 * if Best value has been stabilized*/
			if (stop) {
				System.out.println(" *** Stop! Best fitness has been stabilized.");
				iteration = maxIterations;
			}
		} while (iteration++ < maxIterations);
		showFinalResult();
	}

	private void showFinalResult() {
		System.out.println("( " + particles.get(globalBest).getPosition().getx() + " ^2) " +
		           "( " + particles.get(globalBest).getPosition().gety() + " ^2) " +
		           " = " + particles.get(globalBest).getBestFitness());
		
	}

	private void initializeParticles(Integer particlesListSize) {
		double posX, posY, velX, velY;
		for (int i = 0; i < particlesListSize; i++) {
			double k = random.nextDouble();
			posX = min + (max - min) * k;
			k = random.nextDouble();
			posY = min + (max - min) * k;
			k = random.nextDouble();
			velX = min + (max - min) * k;
			k = random.nextDouble();
			velY = min + (max - min) * k;
			particles.add(new PSOParticle(posX, posY, velX, velY));
		}
	}

	private void moveParticles() {
		for (int i = 0; i < particles.size(); i++) {
			particles.get(i).updateInfo(
					particles.get(globalBest).getPosition().getx(),
					particles.get(globalBest).getPosition().gety());
		}
	}

	private boolean updateGlobalBest() {		
		if (globalBest == null)
			globalBest = 0;
		
		double globalB = globalBest;
		
		for (int i = 0; i < particles.size(); i++)
			if (Double.compare(particles.get(i).getBestFitness(),
					particles.get(globalBest).getBestFitness()) < 0)
				globalBest = i;
		
		/* Stop iterations by returning true 
		 * when global best fitness doesn't change anymore */
		if (Double.compare(globalB, globalBest) == 0) {
			globalBestStabilizedCounter++;
			if (globalBestStabilizedCounter >= 500)
				return true;
			else
				return false;
		}
			
		globalBestStabilizedCounter=0;
		return false;		
	}

	private void updateParticlesBest() {
//		double currentFitness;
		for (int i = 0; i < particles.size(); i++) {
//			currentFitness = particles.get(i).getBestFitness();
			if (particles.get(i).updateBestFitness()) {
//				System.out.println("Best fitness of particle " + i
//						+ " has been updated from " + currentFitness
//						+ " to " + particles.get(i).getFitness());
			}
		}
	}

	private void calculateAllFitness() {
		for (int i = 0; i < particles.size(); i++)
			particles.get(i).calculateFitness();
	}

	private void showProgress(int iteration, int numberOfParticles) {
		System.out.println("Global Best: " +  
				globalBest + " " +
 				particles.get(globalBest).getBestFitness());

		for (int i=0; i<numberOfParticles; i++)
			System.out.println(
					   iteration + " " + i + " " +
					   "( " + particles.get(i).getPosition().getx() + " ^2) " +
			           "( " + particles.get(i).getPosition().gety() + " ^2) " +
			           " = " + particles.get(i).getBestFitness());
	}
}